// MIT License
//
// Copyright (c) 2023-Present - Violet Hansen - (aka HotCakeX on GitHub) - Email Address: spynetgirl@outlook.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// See here for more information: https://github.com/HotCakeX/Harden-Windows-Security/blob/main/LICENSE
//

using System.Collections.Frozen;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using HardenSystemSecurity.Protect;
using Microsoft.Win32;

namespace HardenSystemSecurity.ExploitMitigation;

internal static class Manage
{
	private static readonly List<ProcessMitigationsRecords> CSVProcessMitigations = [];

	/// <summary>
	/// Mapping from MitigationOptions to the corresponding PolicyOptionName
	/// </summary>
	private static readonly FrozenDictionary<MitigationOptions, PolicyOptionName> MitigationToPolicyOptionMapping = new Dictionary<MitigationOptions, PolicyOptionName>
	{
		{ MitigationOptions.DEP, PolicyOptionName.Enable },
		{ MitigationOptions.EmulateAtlThunks, PolicyOptionName.EmulateAtlThunks },
		{ MitigationOptions.ForceRelocateImages, PolicyOptionName.ForceRelocateImages },
		{ MitigationOptions.RequireInfo, PolicyOptionName.RequireInfo },
		{ MitigationOptions.BottomUp, PolicyOptionName.BottomUp },
		{ MitigationOptions.HighEntropy, PolicyOptionName.HighEntropy },
		{ MitigationOptions.StrictHandle, PolicyOptionName.Enable },
		{ MitigationOptions.DisableWin32kSystemCalls, PolicyOptionName.DisableWin32kSystemCalls },
		{ MitigationOptions.AuditSystemCall, PolicyOptionName.Audit },
		{ MitigationOptions.DisableExtensionPoints, PolicyOptionName.DisableExtensionPoints },
		{ MitigationOptions.DisableFsctlSystemCalls, PolicyOptionName.DisableFsctlSystemCalls },
		{ MitigationOptions.AuditFsctlSystemCall, PolicyOptionName.AuditFsctlSystemCalls },
		{ MitigationOptions.BlockDynamicCode, PolicyOptionName.BlockDynamicCode },
		{ MitigationOptions.AllowThreadsToOptOut, PolicyOptionName.AllowThreadsToOptOut },
		{ MitigationOptions.AuditDynamicCode, PolicyOptionName.Audit },
		{ MitigationOptions.CFG, PolicyOptionName.Enable },
		{ MitigationOptions.SuppressExports, PolicyOptionName.SuppressExports },
		{ MitigationOptions.StrictCFG, PolicyOptionName.StrictControlFlowGuard },
		{ MitigationOptions.MicrosoftSignedOnly, PolicyOptionName.MicrosoftSignedOnly },
		{ MitigationOptions.AllowStoreSignedBinaries, PolicyOptionName.AllowStoreSignedBinaries },
		{ MitigationOptions.AuditMicrosoftSigned, PolicyOptionName.Audit },
		{ MitigationOptions.AuditStoreSigned, PolicyOptionName.AuditStoreSigned },
		{ MitigationOptions.EnforceModuleDependencySigning, PolicyOptionName.EnforceModuleDependencySigning },
		{ MitigationOptions.DisableNonSystemFonts, PolicyOptionName.DisableNonSystemFonts },
		{ MitigationOptions.AuditFont, PolicyOptionName.Audit },
		{ MitigationOptions.BlockRemoteImageLoads, PolicyOptionName.BlockRemoteImageLoads },
		{ MitigationOptions.BlockLowLabelImageLoads, PolicyOptionName.BlockLowLabelImageLoads },
		{ MitigationOptions.PreferSystem32, PolicyOptionName.PreferSystem32 },
		{ MitigationOptions.AuditRemoteImageLoads, PolicyOptionName.AuditRemoteImageLoads },
		{ MitigationOptions.AuditLowLabelImageLoads, PolicyOptionName.AuditLowLabelImageLoads },
		{ MitigationOptions.AuditPreferSystem32, PolicyOptionName.AuditPreferSystem32 },
		{ MitigationOptions.EnableExportAddressFilter, PolicyOptionName.EnableExportAddressFilter },
		{ MitigationOptions.AuditEnableExportAddressFilter, PolicyOptionName.AuditEnableExportAddressFilter },
		{ MitigationOptions.EnableExportAddressFilterPlus, PolicyOptionName.EnableExportAddressFilterPlus },
		{ MitigationOptions.AuditEnableExportAddressFilterPlus, PolicyOptionName.AuditEnableExportAddressFilterPlus },
		{ MitigationOptions.EnableImportAddressFilter, PolicyOptionName.EnableImportAddressFilter },
		{ MitigationOptions.AuditEnableImportAddressFilter, PolicyOptionName.AuditEnableImportAddressFilter },
		{ MitigationOptions.EnableRopStackPivot, PolicyOptionName.EnableRopStackPivot },
		{ MitigationOptions.AuditEnableRopStackPivot, PolicyOptionName.AuditEnableRopStackPivot },
		{ MitigationOptions.EnableRopCallerCheck, PolicyOptionName.EnableRopCallerCheck },
		{ MitigationOptions.AuditEnableRopCallerCheck, PolicyOptionName.AuditEnableRopCallerCheck },
		{ MitigationOptions.EnableRopSimExec, PolicyOptionName.EnableRopSimExec },
		{ MitigationOptions.AuditEnableRopSimExec, PolicyOptionName.AuditEnableRopSimExec },
		{ MitigationOptions.SEHOP, PolicyOptionName.Enable },
		{ MitigationOptions.AuditSEHOP, PolicyOptionName.Audit },
		{ MitigationOptions.SEHOPTelemetry, PolicyOptionName.TelemetryOnly },
		{ MitigationOptions.TerminateOnError, PolicyOptionName.TerminateOnError },
		{ MitigationOptions.DisallowChildProcessCreation, PolicyOptionName.DisallowChildProcessCreation },
		{ MitigationOptions.AuditChildProcess, PolicyOptionName.AuditChildProcess },
		{ MitigationOptions.UserShadowStack, PolicyOptionName.UserShadowStack },
		{ MitigationOptions.UserShadowStackStrictMode, PolicyOptionName.UserShadowStackStrictMode },
		{ MitigationOptions.AuditUserShadowStack, PolicyOptionName.AuditUserShadowStack }
	}.ToFrozenDictionary();

	/// <summary>
	/// Mapping from MitigationOptions to PolicyName
	/// </summary>
	private static readonly FrozenDictionary<MitigationOptions, PolicyName> MitigationToPolicyNameMapping = new Dictionary<MitigationOptions, PolicyName>
	{
		{ MitigationOptions.DEP, PolicyName.DEP },
		{ MitigationOptions.EmulateAtlThunks, PolicyName.DEP },
		{ MitigationOptions.ForceRelocateImages, PolicyName.ASLR },
		{ MitigationOptions.RequireInfo, PolicyName.ASLR },
		{ MitigationOptions.BottomUp, PolicyName.ASLR },
		{ MitigationOptions.HighEntropy, PolicyName.ASLR },
		{ MitigationOptions.StrictHandle, PolicyName.StrictHandle },
		{ MitigationOptions.DisableWin32kSystemCalls, PolicyName.SystemCalls },
		{ MitigationOptions.AuditSystemCall, PolicyName.SystemCalls },
		{ MitigationOptions.DisableExtensionPoints, PolicyName.ExtensionPoints },
		{ MitigationOptions.DisableFsctlSystemCalls, PolicyName.SystemCalls },
		{ MitigationOptions.AuditFsctlSystemCall, PolicyName.SystemCalls },
		{ MitigationOptions.BlockDynamicCode, PolicyName.DynamicCode },
		{ MitigationOptions.AllowThreadsToOptOut, PolicyName.DynamicCode },
		{ MitigationOptions.AuditDynamicCode, PolicyName.DynamicCode },
		{ MitigationOptions.CFG, PolicyName.ControlFlowGuard },
		{ MitigationOptions.SuppressExports, PolicyName.ControlFlowGuard },
		{ MitigationOptions.StrictCFG, PolicyName.ControlFlowGuard },
		{ MitigationOptions.MicrosoftSignedOnly, PolicyName.SignedBinaries },
		{ MitigationOptions.AllowStoreSignedBinaries, PolicyName.SignedBinaries },
		{ MitigationOptions.AuditMicrosoftSigned, PolicyName.SignedBinaries },
		{ MitigationOptions.AuditStoreSigned, PolicyName.SignedBinaries },
		{ MitigationOptions.EnforceModuleDependencySigning, PolicyName.SignedBinaries },
		{ MitigationOptions.DisableNonSystemFonts, PolicyName.Fonts },
		{ MitigationOptions.AuditFont, PolicyName.Fonts },
		{ MitigationOptions.BlockRemoteImageLoads, PolicyName.ImageLoad },
		{ MitigationOptions.BlockLowLabelImageLoads, PolicyName.ImageLoad },
		{ MitigationOptions.PreferSystem32, PolicyName.ImageLoad },
		{ MitigationOptions.AuditRemoteImageLoads, PolicyName.ImageLoad },
		{ MitigationOptions.AuditLowLabelImageLoads, PolicyName.ImageLoad },
		{ MitigationOptions.AuditPreferSystem32, PolicyName.ImageLoad },
		{ MitigationOptions.EnableExportAddressFilter, PolicyName.Payload },
		{ MitigationOptions.AuditEnableExportAddressFilter, PolicyName.Payload },
		{ MitigationOptions.EnableExportAddressFilterPlus, PolicyName.Payload },
		{ MitigationOptions.AuditEnableExportAddressFilterPlus, PolicyName.Payload },
		{ MitigationOptions.EnableImportAddressFilter, PolicyName.Payload },
		{ MitigationOptions.AuditEnableImportAddressFilter, PolicyName.Payload },
		{ MitigationOptions.EnableRopStackPivot, PolicyName.Payload },
		{ MitigationOptions.AuditEnableRopStackPivot, PolicyName.Payload },
		{ MitigationOptions.EnableRopCallerCheck, PolicyName.Payload },
		{ MitigationOptions.AuditEnableRopCallerCheck, PolicyName.Payload },
		{ MitigationOptions.EnableRopSimExec, PolicyName.Payload },
		{ MitigationOptions.AuditEnableRopSimExec, PolicyName.Payload },
		{ MitigationOptions.SEHOP, PolicyName.SEHOP },
		{ MitigationOptions.AuditSEHOP, PolicyName.SEHOP },
		{ MitigationOptions.SEHOPTelemetry, PolicyName.SEHOP },
		{ MitigationOptions.TerminateOnError, PolicyName.Heap },
		{ MitigationOptions.DisallowChildProcessCreation, PolicyName.ChildProcess },
		{ MitigationOptions.AuditChildProcess, PolicyName.ChildProcess },
		{ MitigationOptions.UserShadowStack, PolicyName.UserShadowStack },
		{ MitigationOptions.UserShadowStackStrictMode, PolicyName.UserShadowStack },
		{ MitigationOptions.AuditUserShadowStack, PolicyName.UserShadowStack }
	}.ToFrozenDictionary();

	private static List<ProcessMitigationsRecords> GetRecommendedMitigations()
	{

		if (CSVProcessMitigations.Count > 0)
			return CSVProcessMitigations;

		string path = Path.Combine(AppContext.BaseDirectory, "Resources", "Mitigations", "ProcessMitigations.csv");

		using StreamReader reader = new(path);

		// Read the header line
		string? header = reader.ReadLine();

		// Return if the header is null
		if (header is null) return CSVProcessMitigations;

		// Read the rest of the file line by line
		while (!reader.EndOfStream)
		{
			string? line = reader.ReadLine();

			// Skip if the line is null
			if (line is null) continue;

			// Split the line by commas to get the values, that's the CSV's delimiter
			string[] values = line.Split(',');

			// Check if the number of values is 6
			if (values.Length >= 6)
			{
				// Parse the action value from CSV to OPTIONVALUE
				OPTIONVALUE action = values[2].Trim().ToUpperInvariant() switch
				{
					"ON" => OPTIONVALUE.ON,
					"OFF" => OPTIONVALUE.OFF,
					_ => OPTIONVALUE.NOTSET
				};

				// Add a new ProcessMitigationsRecords to the list
				CSVProcessMitigations.Add(new ProcessMitigationsRecords
				(
					programName: values[0],
					mitigation: Enum.Parse<MitigationOptions>(values[1], true),
					action: action,
					removeAllowed: Convert.ToBoolean(values[3], CultureInfo.InvariantCulture),
					id: Guid.Parse(values[4]),
					comment: values[5]
				));
			}
		}

		return CSVProcessMitigations;
	}

	/// <summary>
	/// Gets the current value of a specific mitigation option from AppMitigations
	/// </summary>
	/// <param name="appMitigations">The AppMitigations object to check</param>
	/// <param name="mitigationOption">The mitigation option to check</param>
	/// <returns>The current OPTIONVALUE for the specified mitigation</returns>
	private static OPTIONVALUE GetMitigationValue(AppMitigations appMitigations, MitigationOptions mitigationOption)
	{
		if (!MitigationToPolicyNameMapping.TryGetValue(mitigationOption, out PolicyName policyName))
		{
			throw new InvalidOperationException($"Unknown mitigation type: {mitigationOption}");
		}

		if (!MitigationToPolicyOptionMapping.TryGetValue(mitigationOption, out PolicyOptionName policyOptionName))
		{
			throw new InvalidOperationException($"Unknown mitigation type: {mitigationOption}");
		}

		// Get the policy and check the specific option
		Policy policy = appMitigations.Policies[policyName];
		return policy.GetPolicy(policyOptionName);
	}

	/// <summary>
	/// Adds mitigations for a specific process
	/// </summary>
	/// <param name="processName">The name of the process</param>
	/// <param name="disableList">List of mitigations to disable</param>
	/// <param name="enableList">List of mitigations to enable</param>
	/// <param name="EAFModulesList">List of EAF modules</param>
	/// <param name="isForce">Force flag</param>
	/// <param name="isRemove">Remove flag</param>
	/// <param name="isReset">Reset flag</param>
	/// <returns>Result indicating success or failure</returns>
	private static Result AddMitigationsForProcess(string processName, MitigationOptions[]? disableList, MitigationOptions[]? enableList, string[]? EAFModulesList, string? isForce, bool isRemove, bool isReset)
	{
		try
		{
			if (enableList is null && disableList is null)
				return Result.Failure("The input parameters are invalid. Please specify a list of mitigations to enable or disable for this process.");

			if (processName.Equals(Path.GetFileNameWithoutExtension(processName)))
				processName = (processName + ".exe").ToLowerInvariant();

			Result<List<AppMitigations>> fromRegistryByNameResult = SecurityPolicyRepository.RetrieveSecurityConfigurationFromRegistryByName(processName);
			if (fromRegistryByNameResult.IsFailure)
				return Result.Failure(fromRegistryByNameResult.Error);

			List<AppMitigations> fromRegistryByName = fromRegistryByNameResult.Value;
			AppMitigations? appMitigations = null;
			if (fromRegistryByName.Count is 0)
				appMitigations = new AppMitigations(processName);
			else if (fromRegistryByName.Count > 1)
				return Result.Failure("Multiple mitigation policies found that may match the given process name. Please specify the full path to be matched instead.");
			else
				appMitigations = fromRegistryByName[0];

			return SetProcessMitigationsCommand.setEnableAndDisable(appMitigations, disableList, enableList, EAFModulesList, isForce, isRemove, isReset, isSystemMode: false);
		}
		catch (Exception ex)
		{
			return Result.Failure($"Failed to add mitigations for process: {ex.Message}");
		}
	}

	/// <summary>
	/// Gets the current settings in the registry for a specific process
	/// </summary>
	/// <param name="processName">The name of the process</param>
	/// <returns>Result containing AppMitigations or error</returns>
	private static Result<AppMitigations> GetPolicyFromRegistryByName(string processName)
	{
		try
		{
			Result<List<AppMitigations>> result = SecurityPolicyRepository.RetrieveSecurityConfigurationFromRegistryByName(processName);
			if (result.IsFailure)
				return Result<AppMitigations>.Failure(result.Error);

			List<AppMitigations> policies = result.Value;
			if (policies.Count is 0)
				return Result<AppMitigations>.Failure($"No policies found for process: {processName}");

			if (policies.Count > 1)
				return Result<AppMitigations>.Failure($"Multiple policies found for process: {processName}");

			return Result<AppMitigations>.Success(policies[0]);
		}
		catch (Exception ex)
		{
			return Result<AppMitigations>.Failure($"Failed to get policy from registry by name: {ex.Message}");
		}
	}

	/// <summary>
	/// Applies the secure and recommended process mitigations.
	/// </summary>
	/// <exception cref="InvalidOperationException"></exception>
	internal static void ApplyRecommended()
	{

		// Group the data by ProgramName
		IGrouping<string, ProcessMitigationsRecords>[] groupedMitigations = [.. GetRecommendedMitigations().GroupBy(pm => pm.ProgramName)];

		// Get the current process mitigations from the registry
		string[] allAvailableMitigations = (Registry.LocalMachine
			.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options")
			?.GetSubKeyNames()) ?? throw new InvalidOperationException("Failed to read registry keys.");

		// Loop through each group to remove the mitigations, this way we apply clean set of mitigations in the next step
		Logger.Write("Removing the existing process mitigations", LogTypeIntel.Information);
		foreach (IGrouping<string, ProcessMitigationsRecords> group in groupedMitigations)
		{
			string? fileName = Path.GetFileName(group.Key);

			if (allAvailableMitigations.Contains(fileName, StringComparer.OrdinalIgnoreCase))
			{
				try
				{
					Registry.LocalMachine.DeleteSubKeyTree(
						$@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\{fileName}",
						false);
				}
				catch (Exception ex)
				{
					Logger.Write($"Failed to remove {fileName}, it's probably protected by the system. {ex.Message}", LogTypeIntel.Error);
				}
			}
		}

		foreach (IGrouping<string, ProcessMitigationsRecords> group in groupedMitigations)
		{
			string programName = group.Key;

			Logger.Write($"Adding process mitigations for {programName}", LogTypeIntel.Information);

			MitigationOptions[] enableMitigations = [.. group.Where(g => g.Action is OPTIONVALUE.ON).Select(g => g.Mitigation)];

			MitigationOptions[] disableMitigations = [.. group.Where(g => g.Action is OPTIONVALUE.OFF).Select(g => g.Mitigation)];

			Result addResult = AddMitigationsForProcess(
				programName,
				disableMitigations,
				enableMitigations,
				null,
				null,
				false,
				false);

			if (!addResult.IsSuccess)
			{
				throw new InvalidOperationException(addResult.Error);
			}
		}
	}

	/// <summary>
	/// Creates a complete <see cref="MUnit"/> for each Program whose name is in the CSV file.
	/// </summary>
	/// <param name="ListToAddTo"></param>
	/// <returns></returns>
	internal static void CreateMUnitEntries(List<MUnit> ListToAddTo)
	{
		// Group the data by ProgramName
		IGrouping<string, ProcessMitigationsRecords>[] groupedMitigations = GetRecommendedMitigations().GroupBy(pm => pm.ProgramName).ToArray();

		// Only remove the mitigations that are allowed to be removed
		// It is important for any executable whose name is mentioned as a key in "Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options"
		// by default in a clean Windows installation, to have the RemovalAllowed property of ALL OF ITS MITIGATIONS defined in the Process Mitigations CSV file set to False
		// So regardless of whether mitigations were added by the program, only remove mitigations for processes whose names do not exist in that registry location by default,
		// this will prevent from removing any possible built-in default mitigations.
		// The following removals only affect the registry keys, they do not alter the mitigations defined in Microsoft Defender GUI.
		FrozenSet<string> processMitigationsAllowedToRemove = GetRecommendedMitigations().Where(mitigation => mitigation.RemovalAllowed).Select(x => x.ProgramName).ToFrozenSet();

		foreach (IGrouping<string, ProcessMitigationsRecords> group in groupedMitigations)
		{
			string programName = group.Key;

			MitigationOptions[] enableMitigations = [.. group.Where(g => g.Action is OPTIONVALUE.ON).Select(g => g.Mitigation)];

			MitigationOptions[] disableMitigations = [.. group.Where(g => g.Action is OPTIONVALUE.OFF).Select(g => g.Mitigation)];

			// Nvidia/AMD GPUs issue with StrictCFG: https://github.com/HotCakeX/Harden-Windows-Security/issues/782
			if (string.Equals(programName, "msedge.exe", StringComparison.OrdinalIgnoreCase))
			{
				if (!Hardware.GPUInfoManager.HasOnlyIntelGPU())
				{
					List<MitigationOptions> items = enableMitigations.ToList();
					_ = items.Remove(MitigationOptions.StrictCFG);
					enableMitigations = items.ToArray();
				}
			}

			ListToAddTo.Add(new(
					category: Categories.MicrosoftDefender,
					name: string.Format(GlobalVars.GetStr("ApplyProcessMitigations-MSDefender"), programName),

			applyStrategy: new DefaultApply(() =>
			{

				// Skip applying process mitigations when ARM hardware detected
				if (string.Equals(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"), "ARM64", StringComparison.OrdinalIgnoreCase))
				{
					Logger.Write(GlobalVars.GetStr("ARM64NoticeForProcessMitigations"), LogTypeIntel.Information);

					return;
				}

				Result addResult = AddMitigationsForProcess(
					programName,
					disableMitigations,
					enableMitigations,
					null,
					null,
					false,
					false);

				if (!addResult.IsSuccess)
				{
					throw new InvalidOperationException(addResult.Error);
				}
			}),

			verifyStrategy: new DefaultVerify(() =>
			{
				// Get the mitigations currently applied for the selected program
				Result<AppMitigations> result = GetPolicyFromRegistryByName(programName);
				if (result.IsSuccess)
				{
					AppMitigations currentPolicy = result.Value;

					// Check if all required mitigations from CSV (that should be enabled) are actually enabled in the current policy
					foreach (MitigationOptions requiredMitigation in enableMitigations)
					{
						try
						{
							OPTIONVALUE currentValue = GetMitigationValue(currentPolicy, requiredMitigation);
							if (currentValue != OPTIONVALUE.ON)
							{
								return false;
							}
						}
						catch (InvalidOperationException ex)
						{
							throw new InvalidOperationException($"Error checking mitigation {requiredMitigation} for {programName}: {ex.Message}");
						}
					}

					// Also check that mitigations that should be disabled according to CSV are actually disabled
					foreach (MitigationOptions requiredDisabledMitigation in disableMitigations)
					{
						try
						{
							OPTIONVALUE currentValue = GetMitigationValue(currentPolicy, requiredDisabledMitigation);
							if (currentValue != OPTIONVALUE.OFF)
							{
								return false;
							}
						}
						catch (InvalidOperationException ex)
						{
							throw new InvalidOperationException($"Error checking mitigation {requiredDisabledMitigation} for {programName}: {ex.Message}");
						}
					}

					// All required mitigations from CSV are correctly applied
					return true;
				}
				else
				{
					// No policies found for the program - this means mitigations are not applied
					// If we have mitigations that should be enabled, return false
					return enableMitigations.Length is 0;
				}
			}),

			removeStrategy: new DefaultRemove(() =>
			{
				// Make sure the name of the program whose exploit mitigations are being removed is in the allowed list
				if (!processMitigationsAllowedToRemove.Contains(programName, StringComparer.OrdinalIgnoreCase)) return;

				// Get all of the currently available mitigations from the registry which are executable names
				List<string>? allAvailableMitigations = Registry.LocalMachine
					.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options")?
					.GetSubKeyNames()
					.ToList();

				if (allAvailableMitigations is not null && allAvailableMitigations.Contains(programName, StringComparer.OrdinalIgnoreCase))
				{
					try
					{
						Registry.LocalMachine.DeleteSubKeyTree(
							$@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\{programName}",

							// do not to throw an exception if the specified subkey does not exist. Instead, simply proceed without raising an error.
							throwOnMissingSubKey: false
						);
					}
					catch (Exception ex)
					{
						Logger.Write($"Failed to remove mitigations for {programName}: {ex.Message}", LogTypeIntel.Error);
					}
				}
			}),

			url: "https://learn.microsoft.com/defender-endpoint/exploit-protection-reference",

			deviceIntents: [DeviceIntents.Intent.All],

			id: group.First().ID

			));

		}
	}
}
